const db_manager = require('./db_manager');
const conman = require('./contract_manager');
const web3 = require('./web3');

module.exports = exports = {};

exports.before_parse = function before(program) {
  const deploy = program.command('deploy');
  deploy
    .passThroughOptions()
    .option('--sender <sender> ',
            'eth message sender, or env.ZETHER_ETHACC',
            process.env.ZETHER_ETHACC);

  deploy.command('inner-product-verifier')
    .alias('ipv')
    .description('deploy innerproduct verifier')
    .action(async () => {
      const db = db_manager.load_db(program.opts().datadir);
      const opts = deploy.opts();
      if (!opts.sender) {
        throw Error('no sender account');
      }

      // await web3.eth.personal.unlockAccount(opts.sender, opts.pass, 30);
      const receipt = await conman.deployInnerProductVerifier(opts.sender);
      db.get('inner-product-verifiers').push({
        blockHash: receipt.blockHash,
        blockNumber: receipt.blockNumber,
        contractAddress: receipt.contractAddress,
        from: receipt.sender,
        status: receipt.status,
      }).write();
      console.log('receipt', receipt);
    });

  deploy.command('burn-verifier')
    .alias('bv')
    .description('deploy burn verifier')
    .option('--inner-product-verifier <inner-product-verifier>',
            'inner-product-verifier-address, or latest inner-product-verifier')
    .action(async (options) => {
      const db = db_manager.load_db(program.opts().datadir);
      const opts = {
        ...deploy.opts(),
        ...options,
      };
      if (!opts.sender) {
        throw Error('no sender account');
      }
      if (!opts.innerProductVerifier) {
        const receipt = db.get('inner-product-verifiers').last().value();
        if (!receipt) {
          throw Error('no inner-product-verifier yet');
        }
        opts.innerProductVerifier = receipt.contractAddress;
      }

      // await web3.eth.personal.unlockAccount(opts.sender, opts.pass, 30);
      const receipt = await conman.deployBurnVerifier(opts.innerProductVerifier,
                                                      opts.sender);
      db.get(`burn-verifiers`).push({
        ipvAddress: opts.innerProductVerifier,
        blockHash: receipt.blockHash,
        blockNumber: receipt.blockNumber,
        contractAddress: receipt.contractAddress,
        from: receipt.sender,
        status: receipt.status,
        transactionHash: receipt.transactionHash,
      }).write();
      console.log('receipt:', receipt);
    });

  deploy.command('transfer-verifier-inner')
    .alias('tvi')
    .description('deploy transfer verifier inner')
    .action(async (options) => {
      const db = db_manager.load_db(program.opts().datadir);
      const opts = deploy.opts();
      if (!opts.sender) {
        throw Error('no sender account');
      }

      // await web3.eth.personal.unlockAccount(opts.sender, opts.pass, 30);
      const receipt = await conman.deployTransferVerifierInner(opts.sender);
      db.get(`transfer-verifier-inners`).push({
        blockHash: receipt.blockHash,
        blockNumber: receipt.blockNumber,
        contractAddress: receipt.contractAddress,
        from: receipt.sender,
        status: receipt.status,
      }).write();
      console.log('receipt:', receipt);
    });

  deploy.command('transfer-verifier')
    .alias('tv')
    .description('deploy transfer verifier')
    .option('--inner-product-verifier <inner-product-verifier>',
           'inner-product-verifier, or latest inner-product-verifier')
    .option('--transfer-verifier-inner <transfer-verifier-inner>',
           'transfer-verifier-inner, or latest transfer-verifier-inner')
    .action(async (options) => {
      const db = db_manager.load_db(program.opts().datadir);
      const opts = {
        ...deploy.opts(),
        ...options,
      };
      if (!opts.sender) {
        throw Error('no sender account');
      }
      if (!opts.innerProductVerifier) {
        const receipt = db.get('inner-product-verifiers').last().value();
        if (!receipt) {
          throw Error('no inner-product-verifier yet');
        }
        opts.innerProductVerifier = receipt.contractAddress;
      }
      if (!opts.transferVerifierInner) {
        const receipt = db.get('transfer-verifier-inners').last().value();
        if (!receipt) {
          throw Error('no inner-product-verifier yet');
        }
        opts.transferVerifierInner = receipt.contractAddress;
      }

      // await web3.eth.personal.unlockAccount(opts.sender, opts.pass, 30);
      const receipt = await conman.deployTransferVerifier(opts.innerProductVerifier,
                                                          opts.transferVerifierInner,
                                                          opts.sender);
      db.get(`transfer-verifiers`).push({
        ipvAddress: opts.innerProductVerifier,
        tviAddress: opts.transferVerifierInner,
        blockHash: receipt.blockHash,
        blockNumber: receipt.blockNumber,
        contractAddress: receipt.contractAddress,
        from: receipt.sender,
        status: receipt.status,
      }).write();
      console.log('receipt:', receipt);
    });

  deploy.command('zether <epoch_len>')
    .alias('z')
    .description('deploy new zether contract')
    .option('--burn-verifier <burn-verifier>',
           'burn-verifier-address, or latest burn-verifier')
    .option('--transfer-verifier <transfer-verifier>',
           'transfer-verifier-address, or latest transfer-verifier')
    .action(async (epoch_len, options) => {
      const db = db_manager.load_db(program.opts().datadir);
      const opts = {
        ...deploy.opts(),
        ...options,
      };
      if (!opts.sender) {
        throw Error('no sender account');
      }
      if (!opts.burnVerifier) {
        const receipt = db.get('burn-verifiers').last().value();
        if (!receipt) {
          throw Error('no burn-verifier yet');
        }
        opts.burnVerifier = receipt.contractAddress;
      }
      if (!opts.transferVerifier) {
        const receipt = db.get('transfer-verifiers').last().value();
        if (!receipt) {
          throw Error('no transfer-verifier yet');
        }
        opts.transferVerifier = receipt.contractAddress;
      }      

      const parsed_epoch_len = parseInt(epoch_len, 10);
      if (isNaN(parsed_epoch_len)) {
        throw Error('epoch_len not a number.');
      }

      // await web3.eth.personal.unlockAccount(opts.sender, opts.pass, 30);
      const receipt = await conman.deployZether(parsed_epoch_len,
                                                opts.burnVerifier,
                                                opts.transferVerifier,
                                                opts.sender);
      
      db.set(`zethers.${receipt.contractAddress}`, {
        bvAddress: opts.burnVerifier,
        tvAddress: opts.transferVerifier,
        blockHash: receipt.blockHash,
        blockNumber: receipt.blockNumber,
        contractAddress: receipt.contractAddress,
        from: receipt.from,
        status: receipt.status,
      }).write();
      console.log('receipt:', receipt);
    });

  const info = program.command('info');

  info.command('inner-product-verifier')
    .alias('ipv')
    .description('list all known inner-product-verfiers')
    .action(async () => {
      const db = db_manager.load_db(program.opts().datadir);
      const contracts = db.get('inner-product-verifiers').value();
      if (contracts.length === 0) {
        console.log('no contract yet');
      }
      for (const i in contracts) {
        console.log(i, ':', contracts[i]);
      }
    });

  info.command('burn-verifier')
    .alias('bv')
    .description('list known burn-verifier contracts')
    // .option('-a --ethacc <ethacc>', 'query info of this specific address(on chain)')
    .action(async (options) => {
//      if (!options.ethacc) {
        const db = db_manager.load_db(program.opts().datadir);
        const contracts = db.get('burn-verifiers').value();
        if (contracts.length === 0) {
          console.log('no contract yet');
        }
        for (const i in contracts) {
          console.log(i, ':', contracts[i]);
        }
//      } else {
//        let ip = await conman.info_burn(options.ethacc);
//        console.log('ip address', ip);
//      }
    });

  info.command('transfer-verifier-inner')
    .alias('tvi')
    .description('get transfer-verifier contract info')
    .action(async (options) => {
      const db = db_manager.load_db(program.opts().datadir);
      const contracts = db.get('transfer-verifier-inners').value();
      if (contracts.length === 0) {
        console.log('no contract yet');
      }
      for (const i in contracts) {
        console.log(i, ':', contracts[i]);
      }
    });

  info.command('transfer-verifier')
    .alias('tv')
    .description('get transfer-verifier contract info')
//     .option('-a --ethacc <ethacc>', 'query info of this specific address(on chain)')
    .action(async (options) => {
//       if (!options.ethacc) {
        const db = db_manager.load_db(program.opts().datadir);
        const contracts = db.get('transfer-verifiers').value();
        if (contracts.length === 0) {
          console.log('no contract yet');
        }
        for (const i in contracts) {
          console.log(i, ':', contracts[i]);
        }
//      } else {
//        let res = await conman.info_transfer(options.ethacc);
//        console.log(`ipv address: ${res[0]}\ntvi address: ${res[1]}`);
//      }
    });

  info.command('zether')
    .alias('z')
    .description('get zether contract info')
    .option('-a --ethacc <ethacc>', 'query info of this address on chain')
    .action(async (options) => {
      if (!options.ethacc) {
        const db = db_manager.load_db(program.opts().datadir);
        const contracts = db.get('zethers').cloneDeep().value();
        const entries = Object.entries(contracts);
        if (entries.length === 0) {
          console.log('no contract yet');
        }
        for (const [ethacc, contract] of entries) {
          console.log(ethacc, ':', {
            contractAddress: contract.contractAddress,
            blockHash: contract.blockHash,
            blockNumber: contract.blockNumber,
            owner: contract.from,
          });
        }
      } else {
        const res = await conman.info_zether(options.ethacc);
        console.log(`owner: ${res[0]}\nb_total: ${res[1]}\nepoch_len: ${res[2]}`);
        console.log(`zth accounts:`, res[3]);
        console.log(`nonces:`, res[4]);
      }
    });

  // info.command('zether <zether_address>')
  //   .description('query zether contract')
  //   .action((zether_address) => {
  //     const db = db_manager.load_db(program.opts().datadir);
  //     const contracts = db.get('contracts').cloneDeep().value();
  //     const entries = Object.entries(contracts);
  //     if (entries.length === 0) {
  //       console.log('no contract yet');
  //     }
  //     for (const [ethacc, contract] of entries) {
  //       console.log(ethacc, ':', {
  //         contractAddress: contract.contractAddress,
  //         blockHash: contract.blockHash,
  //         blockNumber: contract.blockNumber,
  //         sender: contract.sender,
  //       });
  //     }
  //   });


  // comm.command('info <contract_address>')
  //   .description('print details of the contract')
  //   .action(async (contract_address) => {
  //     try {
  //       const res = await conman.info(contract_address);
  //       console.log(`sender: ${res[0]}`);
  //       console.log(`total coins: ${res[1]}`);
  //       console.log(`epoch len: ${res[2]}`);
  //       if (!res[3]) {
  //         console.log('registered yhashes: empty set');
  //       } else {
  //         console.log('registered yhashes:');
  //         for (let i in res[3]) {
  //           console.log(`${i}: ${res[3][i]}`);
  //         }
  //       }
  //     } catch (err) {
  //       throw err.message;
  //     }
  //   });
};

exports.after_parse = function after(program) {
};
